#!/usr/bin/python
import os
import sys
import glob
import math
import optparse
import subprocess
import tempfile

def get_options():
    # Parse the arguments to support various ways of doing things.
    parser = optparse.OptionParser(usage="Usage: %prog [options] input1.pdf input2.pdf ...")
    parser.add_option("-d", "--dimensions", dest="dimensions",
                      default="17inx11in",
                      help="DIMENSIONS of the output pages. Default units are inches. Default:17inx11in", metavar="DIMENSIONS")
    parser.add_option("-v", "--verbose",
                  help="Whether or not to show latex output.",
                      action="store_true", dest="verbose")
    parser.add_option("-f", "--pdf2ps", dest="pdf2ps", default="pdf2ps",
                      help="PDF2PS command line. Default: pdf2ps",
                      metavar="PDF2PS")
    parser.add_option("-o", "--output", dest="output",
                      default=None,
                      help="OUTPUT of the pdf file. Use - to output to stdout.",
                      metavar="OUTPUT")
    parser.add_option("-p", "--pdflatex", dest="pdflatex",
                      default="pdflatex",
                      help="PDFLATEX program command. Default: pdflatex",
                      metavar="PDFLATEX")
    parser.add_option("-t", "--tempdir", dest="tempdir",
                      default=None,
                      help="Temporary directory. Default: system default.")
    
    (options, args) = parser.parse_args()
    if not options.output:
        parser.error("Required option OUTPUT not supplied.")
    if not args:
        parser.error("No input PDFs given.")
    pdfs = []
    for arg in args:
        if not arg.upper().endswith('.PDF'):
            parser.error("Invalid input file: %s" % arg)
        if arg.upper() == options.output.upper():
            parser.error("Input file %s cannot be the same file as the output.")
        if arg.startswith('/'):
            pdfs.append(arg)
        else:
            pdfs.append(os.path.join(os.getcwd(), arg))
        if not os.path.exists(pdfs[-1]):
            parser.error("Input file %s does not exist." % pdfs[-1])


    params = {
        'input': pdfs,
        'output': options.output or '-',
        'tmpdir': options.tempdir,
        'pdf2ps': options.pdf2ps,
        'pdflatex': options.pdflatex,
        'tempdir': options.tempdir,
        'verbose': options.verbose,
        }
    width, height = options.dimensions.split('x')
    if width[-1].isdigit():
        width += 'in'
    if height[-1].isdigit():
        height += 'in'
    params['width'] = width
    params['height'] = height
    return params

def create_ordering(pdfs, pdf_pages):
    # Given a number of pages, how do we order the pages
    # to provide the correct layout?
    page_labels = []
    for docnum, pdf in enumerate(pdfs):
        page_labels.extend([(docnum, i + 1)
                            for i in xrange(pdf_pages[pdf])])
    num = len(page_labels)

    while len(page_labels) % 4:
        page_labels.append(None)

    spreads = int(math.ceil(num / float(4)))
    up_pages = spreads * 4
    page_ordering = []
    for spread in xrange(spreads * 2):
        left, right = page_labels[spread], page_labels[up_pages - spread - 1]
        if spread % 2 == 0:
            # Even spread, so we go right to left
            right, left = left, right
        page_ordering.extend((left, right))
    pdf_listings = []
    curdoc = None
    curlist = []
    for page in page_ordering:
        if page is None:
            curlist.append('{}')
        elif curdoc == page[0]:
            curlist.append(page[1])
        else:
            if curdoc is not None:
                pdfname = pdfs[curdoc]
                pdf_listings.append((pdfname, ','.join(map(str, curlist))))
                curlist = []
            if page is None:
                curdoc = None
                curlist.append('{}')
            else:
                curdoc = page[0]
                curlist.append(page[1])
    if curlist and curdoc is not None:
        pdfname = pdfs[curdoc]
        pdf_listings.append((pdfname, ','.join(map(str, curlist))))
    return pdf_listings

def create_tex_file(options, pdf_listings):
    # Our latex file requires the use of the package pdfpages.
    f = tempfile.NamedTemporaryFile(suffix='.tex', dir=options['tempdir'])
    includes = []
    for pdf, pages in pdf_listings:
        includes.append('%s,%s' % (pdf, pages))
    options['includes'] = '\\includepdfmerge[nup=2x1]{\n%s\n}' % ',\n'.join(includes)

    f.write(r"""\documentclass{article}
\usepackage[paperwidth=%(width)s,paperheight=%(height)s,margin=0pt]{geometry}
\usepackage[final]{pdfpages}
\begin{document}
%(includes)s
\end{document}
\endinput

""" % options)
    f.flush()
    return f

def get_pdf_pagecount(pdf2ps, pdf):
    # Count number of pages in C<pdf>
    p = subprocess.Popen([pdf2ps, '-sOutputFile=-', pdf], stdout=subprocess.PIPE)
    num_pages = 0
    for line in p.stdout:
        if line.find('%%Page: ') != -1:
            num_pages += 1
    return num_pages

def get_pagecounts(options):
    input_pdfs = {}
    for pdf in options['input']:
        input_pdfs[pdf] = get_pdf_pagecount(options['pdf2ps'], pdf)
    return input_pdfs

def run_latex(pdflatex, output, tf, verbose):
    name = tf.name
    base = name[:-4]
    pipe = subprocess.PIPE
    if os.path.exists('/dev/null'):
        null = open('/dev/null', 'w')
    else:
        # Probably windows...
        null = open('nul', 'w')
    p = subprocess.Popen([pdflatex, '-interaction=nonstopmode', '-output-format', 'pdf', name], stdout=pipe, stderr=pipe)
    stdout, stderr = p.communicate()
    if p.returncode:
        if not verbose:
            raise RuntimeError("Latex failed...returned %s (use -v flag)" % p.returncode)
        else:
            tf.seek(0)
            log = base + '.log'
            lf = open(log)
            log = lf.read()
            lf.close()
            sys.stderr.write("Latex failed! Output below:\n========\n%s\n%s\nTex Contents:\n=========\n%s\nLog:\n=========\n%s\n" %
                             (stdout, stderr, tf.read(), log))
            sys.exit(1)
    if output == '-':
        pf = open(base + '.pdf')
        sys.stdout.write(pf.read())
        pf.close()
    else:
        os.rename(base + '.pdf', output)
    try:
        null.close()
        for f in glob.glob(base + '.*'):
            os.remove(f)
        tf.close()
    except:
        pass

def main():
    options = get_options()

    pagecounts = get_pagecounts(options)
    pdf_listings = create_ordering(options['input'], pagecounts)    

    tmpfile = create_tex_file(options, pdf_listings)
    run_latex(options['pdflatex'], options['output'], tmpfile, options['verbose'])

if __name__ == '__main__':
    main()
